#include "RenderController.h"
#include "GLRenderComp.h"
#include <QApplication>
#include <QtMath>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
#include <QRandomGenerator>
#endif

RenderController* RenderController::getInstance()
{
	static RenderController instance;
	return &instance;
}
RenderController::RenderController() { }
RenderController::~RenderController() { }
void RenderController::init(const QSize& size)
{
	initializeOpenGLFunctions();
	initShaderProgram();

	mIconRender		 = new GLIconRender();
	mLineRender		 = new GLLineRender();
	mBillBoardRender = new GLBillBoardRender();

	glClearColor(0.0f, 0.8f, 1.0f, 1.0f);
	mViewSize	= size;
	mBackground = new GLBackground();
	mBackground->initTexture();
	mBackground->initData();

	mRect = new GLRect();
	mRect->initData();

	updateCount(1);
}
void RenderController::unInit()
{
	safeDelete(mBackground);
	safeDelete(mRect);
	safeDelete(mColorProgram);
	safeDelete(mTextureProgram);
	safeDelete(mTextureInstanceProgram);
	safeDelete(mColorInstanceProgram);

	safeDelete(mIconRender);
	safeDelete(mLineRender);
	safeDelete(mBillBoardRender);
}
void RenderController::paint()
{
	glClearColor(0.0f, 0.8f, 1.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDepthMask(GL_FALSE);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	mBackground->paint(mTextureProgram, mMVPMat);
	mLineRender->paint(mColorInstanceProgram, mMVPMat);
	mIconRender->paint(mTextureInstanceProgram, mMVPMat);
	mBillBoardRender->paint(mTextureInstanceProgram, mMVPMat);
	mRect->paint(mColorProgram, mMVPMat);
}

void RenderController::controlMoveUpdate(const QString& timeStr)
{
	makeCurrent();
	{
		mBillBoardRender->updateSelectedText(timeStr);
		mBillBoardRender->updateData();
	}
	doneCurrent();
	renderRequest();
}

void RenderController::controlCompTranslate(int tx, int ty)
{
	bool changed = false;
	for (auto comp : mComps)
	{
		if (comp->mBillBoard->isSelected())
		{
			auto offset	 = QPoint(tx, ty);
			auto iconPos = comp->mIcon->pos();
			iconPos += offset;
			comp->mIcon->setPos(iconPos);

			auto boardPos = comp->mBillBoard->pos();
			boardPos += offset;
			comp->mBillBoard->setPos(boardPos);

			auto lineStart = comp->mLine->start();
			auto lineStop  = comp->mLine->stop();
			lineStart += offset;
			lineStop += offset;
			comp->mLine->setStart(lineStart);
			comp->mLine->setStop(lineStop);
			changed = true;
		}
	}
	if (changed)
	{
		makeCurrent();
		mBillBoardRender->updateData();
		mIconRender->updateData();
		mLineRender->updateData();
		doneCurrent();
		renderRequest();
	}
}

bool RenderController::updateCount(int v)
{
	if (v <= 0)
	{
		return false;
	}
	if (v == mCount)
	{
		return false;
	}
	if (v < mCount)
	{
		QList<GLRenderComp*> comps = mComps.mid(0, v);
		for (int i = v; i < mCount; i++)
		{
			delete mComps[i];
		}
		mComps = comps;
	}
	else
	{
		for (int i = mCount; i < v; i++)
		{
			auto comp = new GLRenderComp(i);
			mComps.append(comp);
		}
	}
	mCount = v;
	initCompDatas();
	return true;
}
static QPoint genBillBoardPos(const QPoint& iconPos)
{
	QPoint ret;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
	int distanceX = QRandomGenerator::global()->generate() % 24 + 128;
	int distanceY = QRandomGenerator::global()->generate() % 24 + 128;
#else
	int distanceX = qrand() % 24 + 128;
	int distanceY = qrand() % 24 + 128;
#endif
	ret.setX(iconPos.x() + distanceX);
	ret.setY(iconPos.y() + distanceY);
	return ret;
}
void RenderController::initCompDatas()
{
	auto backSize	= mBackground->backgroundSize();
	auto windowSize = viewSize() * qApp->devicePixelRatio();
	int	 xOffset	= (backSize.width() - windowSize.width()) / 2;
	int	 yOffset	= (backSize.height() - windowSize.height()) / 2;

	float deltaAngle = 3.14 * 2 / mCount;
	float r			 = 1500;

	for (int i = 0; i < mComps.size(); i++)
	{
		auto  comp	= mComps.at(i);
		float fi	= i * deltaAngle;
		auto  point = QPoint { xOffset + (int)(r * qCos(fi)), yOffset + (int)(r * qSin(fi)) };
		comp->mIcon->setPos(point);
		auto boardPos  = genBillBoardPos(point);
		auto boardSize = QSize(300, 240);
		comp->mBillBoard->setPos(boardPos, false);
		comp->mBillBoard->setSize(boardSize, false);
		comp->mLine->setStart(point);
		comp->mLine->setStop(boardPos);
	}
	mLineRender->initVAO();
	mLineRender->updateData();
	mIconRender->initVAO();
	mIconRender->updateData();
	if (mBillBoardRender)
	{
		safeDelete(mBillBoardRender);
		mBillBoardRender = new GLBillBoardRender();
	}
	mBillBoardRender->initVAO();
	mBillBoardRender->updateData();
}
void RenderController::setScale(qreal v, bool update)
{
	mScale = v;
	mBackground->setScale(v);
	if (update)
	{
		mBackground->updateData();
		mLineRender->updateData();
		mIconRender->updateData();
		mBillBoardRender->updateData();
	}
}
QSize RenderController::backgroundSize() const
{
	return mBackground->backgroundSize();
}

void RenderController::setViewSize(const QSize& v, bool update)
{
	mViewSize = v;
	if (update)
	{
		mBackground->updateData();
		mLineRender->updateData();
		mIconRender->updateData();
		mBillBoardRender->updateData();
	}
}

void RenderController::initShaderProgram()
{
	// 各种 shader 分别创建对应的program,渲染过程中需要哪个用哪个,不要实时创建(会耗大量CPU和内存，会内存泄漏)
	genShaderProgram<ShaderSource_Color_V330>(mColorProgram);
	genShaderProgram<ShaderSource_Texture_V330>(mTextureProgram);
	genShaderProgram<ShaderSource_ColorInstance_V330>(mColorInstanceProgram);
	genShaderProgram<ShaderSource_TextureInstance_V330>(mTextureInstanceProgram);
}
